在以太坊虛擬機(Ethereum Virtual Machine,簡稱 EVM)中,堆疊(stack)是一個重要的資料結構,遵循後入先出(LIFO)的原則。程式可以透過 PUSH 將資料放入堆疊,並使用 POP 從堆疊中取出資料。
首先,我們會將程式碼中的 opcode 挑出,以便進一步執行相應的操作。
class EVM:
def __init__(self, code) -> None:
self.code = code
self.pc = 0
self.stack = []
# Program Counter Return Execute Opcodes
def next(self):
opCode = self.code[self.pc]
self.pc += 1
return opCode
# Run EVM until code end
def execute(self):
while self.pc < len(self.code):
opCode = self.next()
# Stack Operation
if opcodes.PUSH0 <= opCode and opcodes.PUSH32 or opCode == opcodes.POP:
stack.Stack(self, opCode)
# main
if __name__ == '__main__':
code = b"\x01\x01"
evm = EVM(code)
evm.execute()
這段 code 主要會分類會使用的 opcode,將該指令傳入 class 處理當前 stack
if opcodes.PUSH0 <= opCode and opcodes.PUSH32 or opCode == opcodes.POP:
stack.Stack(self, opCode)
而在這部分我們會實現 stack 的 class,由 EVM 將 code 傳進來處理,而 Stack 會根據 opcode 來計算要傳入多少大小的字節進 evm 的 stack
PUSH 指令允許我們將數據推送到堆疊中。以太坊升級後,引入了多個 PUSH 指令,可以將不同大小的數據推送到堆疊中。
以太坊上海升級後新增了 PUSH0 ,該 opcode 負責將 0 加入 stack,而 PUSH1 (0x60) - PUSH32(0x7F),都可以向以下去實現,data 就是 PUSH 的 size 而這個 size 會由 OPCODE - PUSH1 的 opcode 來推斷要加入多少byte 進 stack,我們透過計算出 PUSH 指令應推送的字節大小,然後進行推送操作。
size = opCode - PUSH1 + 1
push(size)
def push(self, size):
# 從程式碼中取得要推送的數據
data = self.evm.code[self.evm.pc : self.evm.pc + size]
value = int.from_bytes(data, byteorder='big')
self.evm.stack.append(value)
self.evm.pc += size
POP 指令用於從堆疊中移除數據。
def pop(self):
return self.evm.stack.pop()
這個 pop
函數可以讓我們從堆疊中移除頂部的數據。
#
# POP Operations
#
POP = 0x50
#
# Push Operations
#
PUSH0 = 0x5F
PUSH1 = 0x60
PUSH2 = 0x61
PUSH3 = 0x62
PUSH4 = 0x63
PUSH5 = 0x64
PUSH6 = 0x65
PUSH7 = 0x66
PUSH8 = 0x67
PUSH9 = 0x68
PUSH10 = 0x69
PUSH11 = 0x6A
PUSH12 = 0x6B
PUSH13 = 0x6C
PUSH14 = 0x6D
PUSH15 = 0x6E
PUSH16 = 0x6F
PUSH17 = 0x70
PUSH18 = 0x71
PUSH19 = 0x72
PUSH20 = 0x73
PUSH21 = 0x74
PUSH22 = 0x75
PUSH23 = 0x76
PUSH24 = 0x77
PUSH25 = 0x78
PUSH26 = 0x79
PUSH27 = 0x7A
PUSH28 = 0x7B
PUSH29 = 0x7C
PUSH30 = 0x7D
PUSH31 = 0x7E
PUSH32 = 0x7F
class Stack:
def __init__(self, evm, opCode) -> []:
self.evm = evm
if opCode == PUSH0:
evm.stack.append(0)
elif PUSH1 <= opCode and opCode <= PUSH32:
size = opCode - PUSH1 + 1
self.push(size)
elif opCode == POP:
self.pop()
def push(self, size):
data = self.evm.code[self.evm.pc : self.evm.pc + size]
value = int.from_bytes(data, byteorder='big')
self.evm.stack.append(value)
self.evm.pc += size
def pop(self):
return self.evm.stack.pop()
現在,你已經有了基本的 PUSH 和 POP 指令的實現,可以繼續擴充你的 EVM 功能,如 DUP 和 SWAP 指令。如果需要更多幫助,隨時提問!